/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package business.service;

import com.nmp.common.CommonUtil;
import com.nmp.common.RestUtil;
import com.nmp.common.UserData;
import com.nmp.model.UserModel;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.log4j.Logger;
import org.json.simple.parser.JSONParser;

/**
 *
 * @author ngotandieu
 */
public class FBClientService {

    private static FBClientService instance = null;
    private static final Lock iLock = new ReentrantLock();
    protected final Logger logger = Logger.getLogger(this.getClass());

    private static final String FB_APP_NAME = "fb";
    private static final String FB_SECRECT_KEY = "520f79d51b6cb044dc112e565ebb22b9";
    private static final String FB_APP_ID = "1509402355969408";
    
    public static FBClientService getInstance() {
        iLock.lock();
        try {
            if (instance == null) {
                instance = new FBClientService();
            }
            return instance;
        } finally {
            iLock.unlock();
        }
    }
    
    public String createFBAccount(UserData userData) {
        String id = "";
        try {
            userData.setApp(FB_APP_NAME);
            userData.setCreated(System.currentTimeMillis());
            userData.setDob(0);
            // random password
            String pass = RandomStringUtils.randomAlphanumeric(10);
            userData.setPasswdMD5(CommonUtil.md5(pass));
            
            UserModel userModel = UserModel.getInstance();
            id = userModel.addUser(userData);
        } catch (Exception ex) {
            logger.info("createFBAccount: " + ex.getMessage(), ex);
        }
        return id;
    }

    public UserData inspectAccessTokens(String accessToken) {
        UserData userData = null;
        try {
            String restUrl = "https://graph.facebook.com/me?access_token=" + accessToken;
            Map<String, String> httpsRest = RestUtil.httpsRest(restUrl);
            if(httpsRest != null) {
                String code = httpsRest.get("respCode");
                if(code.equals("200")) {
                    String data = httpsRest.get("data");
                    JSONParser parser = new JSONParser();
                    Map<String, String> apiData = (Map) parser.parse(data);
                    
                    userData = new UserData();
                    userData.setApp_user_id("fb." + apiData.get("id"));
                    userData.setEmail(apiData.get("email"));
                    int gender = apiData.get("gender") == null ? 0 : (apiData.get("gender").equalsIgnoreCase("mail") ? 1 : 2);
                    userData.setGender(gender);
                    userData.setLink(apiData.get("link"));
                    userData.setName(apiData.get("name"));
                    
                    String fbAvatarUrl = "https://graph.facebook.com/" + apiData.get("id") + "/picture?redirect=0&type=square";
                    Map<String, String> respAvt = RestUtil.httpsRest(fbAvatarUrl);
                    String avatar = "";
                    if(respAvt.get("respCode").equals("200")) {
                        String restAvt = respAvt.get("data");
                        Map dataAvt = (Map) parser.parse(restAvt);
                        dataAvt = (Map) dataAvt.get("data");
                        boolean is_silhouette = (boolean) dataAvt.get("is_silhouette");
                        if(!is_silhouette) {
                            avatar = (String) dataAvt.get("url");
                        }
                    }
                    userData.setAvatar(avatar);
                    
                    return userData;
                }
            }
        } catch (Exception ex) {
            logger.error("inspectAccessTokens: " + ex.getMessage(), ex);
        }
        return userData;
    }
    
    public boolean inspectAccessTokens(String userId, String inputToken, String accessToken) {
        try {
            String restUrl = "https://graph.facebook.com/debug_token?input_token=" + inputToken + "&access_token=" + accessToken;
            Map<String, String> httpsRest = RestUtil.httpsRest(restUrl);
            if(httpsRest != null) {
                String code = httpsRest.get("respCode");
                if(code.equals("200")) {
                    String data = httpsRest.get("data");

                    JSONParser parser = new JSONParser();
                    Map apiResp = (Map) parser.parse(data);
                    Map<String, String> apiData = (Map<String, String>) apiResp.get("data");
                    String user_id = apiData.get("user_id");
                    String app_id = apiData.get("app_id");
                    if (!user_id.equalsIgnoreCase(userId) || app_id.equalsIgnoreCase(FB_APP_ID)) {
                        return true;
                    }
                }
            }
        } catch (Exception ex) {
            logger.error("inspectAccessTokens: " + ex.getMessage(), ex);
        }
        return false;
    }
    
    public String getAppAccessToken() {
        String apps_access_token = "";
        try {
            String restUrl = "https://graph.facebook.com/oauth/access_token?client_id=" + FB_APP_ID + "&client_secret=" + FB_SECRECT_KEY + "&grant_type=client_credentials";
            Map<String, String> httpsRest = RestUtil.httpsRest(restUrl);
            if(httpsRest != null) {
                String code = httpsRest.get("respCode");
                if(code.equals("200")) {
                    String data = httpsRest.get("data");
                    apps_access_token = data.replace("access_token=", "");
                }
            }
        } catch (Exception ex) {
            logger.error("getAppAccessToken: " + ex.getMessage(), ex);
        }
        return apps_access_token;
    }
    
    private String base64_url_decode(String input) throws IOException {
        return new String(new Base64(true).decode(input));
    }

    public Map parseSignedRequest(String input) throws Exception {
        String[] split = input.split("[.]", 2);

        String encoded_sig = split[0];
        String payload = split[1];
        JSONParser parser = new JSONParser();
        Map envelope = (Map) parser.parse(base64_url_decode(payload));

        String algorithm = (String) envelope.get("algorithm");
        if (!algorithm.equals("HMAC-SHA256")) {
            throw new Exception("Invalid request. (Unsupported algorithm.)");
        }

        if (((Long) envelope.get("issued_at")) < System.currentTimeMillis() / 1000 - 3600) {
            throw new Exception("Invalid request. (Too old.)");
        }

        byte[] key = FB_SECRECT_KEY.getBytes();
        SecretKey hmacKey = new SecretKeySpec(key, "HMACSHA256");
        Mac mac = Mac.getInstance("HMACSHA256");
        mac.init(hmacKey);
        byte[] digest = mac.doFinal(payload.getBytes());

        if (!Arrays.equals(new Base64(true).decode(encoded_sig), digest)) {
            throw new Exception("Invalid request. (Invalid signature.)");
        }

        return envelope;
    }
}
